(function(window){
	var atto = window.atto = {};

	// console 객체가 없을 경우
	if (!window.console) {
		window.console = {
			log : function(){},
			dir : function(){}
		};
	} else if (!window.console.dir){
		window.console.dir = function(){};
	}
	
	// 도메인 설정
	document.domain = 'travelro.co.kr';
	
	/**
	 * 캐시방지를 위한 문자열
	 */
	var _updateDate = '20120928';
	
	/**
	 * 로딩중인 스크립트의 상태를 가지고 있는 맵
	 * key {string} 객체이름 ex) atto.util.Pagination
	 * value {number} 1:로딩중, 2:로딩완료
	 */
	var _loadStatusMap = {};
	
	/**
	 * object name 별로 
	 * 대기중인 함수 Array를 담고 있는 Map
	 */
	var _deferedFunctionsMap = {};
	
	/**
	 * 구글맵 로드시 추가할 라이브러리
	 */
	var _googleMapLibrary = '';
	
	/**
	 * 해당 객체의 javascript 파일을 동적으로 로드한다.
	 * @param name {string} 로드할 객체의 이름
	 * @private
	 */
	function _loadScript(name) {
		if (_loadStatusMap[name] && _loadStatusMap[name] > 0) {
			return;
		}
		_loadStatusMap[name] = 1;
		var script = document.createElement('script');
		script.type = 'text/javascript';
		if (name == 'google.maps') {
			var scriptSrc = 'http://maps.google.com/maps/api/js?sensor=true&callback=atto.onloadGoogleMaps';
			if (_googleMapLibrary) {
				scriptSrc += '&libraries=' + _googleMapLibrary;
			}
			script.src = scriptSrc; 
		} else {
			var bAtto = /^atto\./.test(name);
			var scriptSrc;
			if (bAtto) {
				scriptSrc = '/js/' + name.replace(/\./g, '/') + '.js';
			} else {
				scriptSrc = '/js/' + name;
			}
			if (_updateDate) {
				scriptSrc += '?_=' + _updateDate;
			}
			script.src = scriptSrc;
			
			script.onload = script.onreadystatechange = function(){
	        	if (_loadStatusMap[name]==2 || (script.readyState && script.readyState != 'complete' && script.readyState != 'loaded')) {
	        		return;
	            }
	        	if (!bAtto) {
	        		// atto 네임스페이스에 있는 객체들은 define을 통해 정의되었을때 _callDeferedFunctions를 호출한다.
	        		_callDeferedFunctions(name);
	        	}
	        };
		}
		document.getElementsByTagName("head")[0].appendChild(script);
	}
	
	/**
	 * 해당 name에 대해 대기중인 함수들을 모두 실행시킨다. 
	 */
	function _callDeferedFunctions(name) {
		var fnArr = _deferedFunctionsMap[name];
		while (fnArr && fnArr.length > 0) {
			fnArr.pop().call();
		}
		_loadStatusMap[name] = 2;
	}
	
	/**
	 * 구글 jsapi 로드 완료시 현재 위치를 업데이트 할 지 여부
	 */
	var _bUpdateRegion = false;
	
	/**
	 * 구글맵 API가 호출될 때 추가할 라이브러리를 설정한다.
	 */
	atto.setGoogleMapLibrary = function(name){
		_googleMapLibrary = name;		
	};
	
	/**
	 * 구글 jsapi 가 로드된 후 호출하게될 함수명. (직접호출용이 아님. 외부에서 호출)
	 */
	atto.onloadGoogleMaps = function(){
		_callDeferedFunctions('google.maps');
		
		/*
		if (_bUpdateRegion && 
			google.loader.ClientLocation &&
			google.loader.ClientLocation.address.country_code &&
			google.loader.ClientLocation.address.region) {
				$.post('/member/updateRegion.json', {
					city : google.loader.ClientLocation.address.region
				});
		}
		*/
	};
	
	/**
	 * 구글 jsapi 완료시 현재 위치를 업데이트 하도록 설정한다.
	 */
	atto.updateRegion = function() {
		_bUpdateRegion = true;
	};
	
	/**
	 * names 배열에 있는 이름의 객체가 존재할때까지 대기하다가
	 * 모두 생성된 후에 fn 을 실행한다. 
	 * 
	 * names 에 들어갈 이름 형식
	 * 1. 'atto.xxxx.xxx' 의 형식의 네임스페이스가 포함된 객체명
	 * 2. 'lib/xxxxxx.js' 형식의 경로명 (base 경로는 '/js/')
	 * 3. 'google.maps' : 구글맵스
	 * 4. 'domReady' : domReady 이벤트
	 * 
	 * @param names {array} 객체이름 목록
	 * @param fn {function} 모든 객체가 생성된 후 실행될 함수
	 */
	atto.require = function(names, fn){
		var defers = [];
		var deferCount = 0;
		$.each(names, function(idx, name){
			if (!_loadStatusMap[name] || _loadStatusMap[name]==1) {
				deferCount++;
				defers.push(name);
			}
		});
		if (deferCount > 0) {
			$.each(defers, function(idx, name){
				var fnArr = _deferedFunctionsMap[name];
				if (!fnArr) {
					_deferedFunctionsMap[name] = fnArr = [];
				}
				fnArr.push(function(){
					deferCount--;
					if (deferCount==0) {
						fn.call();
					}
				});
				if (name != 'domReady') {
					_loadScript(name);
				}
			});
		} else {
			fn.call();
		}
	};
	
	
	/**
	 * 주어진 이름의 객체를 생성한다.
	 * 네임스페이스(부모객체)가 없으면 새로 생성한다.
	 * requires 로 넘어온 객체명 목록이 존재할 때까지 대기한 후 (atto.require 호출)
	 * fn 을 실행한다.  
	 * 
	 * @param name {string} 객체명
	 * @param requires {array} fn 실행되기 전 존재해야 할 객체명 배열 
	 * @param fn {function} 실행될 함수
	 */
	atto.define = function(name, requires, fn){
		var parts = name.split('.');
		var base = window;
		var objName;
		$.each(parts, function(idx, part){
			if (idx < parts.length-1) {
				if (typeof base[part] === 'undefined') {
					base[part] = {};
				}
				base = base[part];
			} else {
				objName = part;
			}
		});
		atto.require(requires, function(){
			var object = fn.call(base, base);
			if (base[objName]) {
				$.extend(base[objName], object);	
			} else {
				base[objName] = object;
			}
			_callDeferedFunctions(name);
		});
	};
	
	/**
	 * 주어진 이름의 객체가 존재하는지 확인한다.
	 * @return {boolean}
	 */
	atto.exist = function(name){
		var parts = name.split('.');
		var base = window;
		var objName;
		var exist = true;
		$.each(parts, function(idx, part){
			if (idx < parts.length) {
				if (typeof base[part] === 'undefined') {
					exist = false;
					return false;
				} else {
					base = base[part];
				}
			}
		});
		return exist;
	};
	
	/**
	 * CSS 동적 로딩
	 */
	atto.loadCSS = function(path){
		var addedPath = path + '?' + new Date().getTime();
		if (document.createStyleSheet){
            document.createStyleSheet(addedPath);
        } else {
        	$('head').append($('<link rel="stylesheet" href="' + addedPath + '" type="text/css" />'));
        }
	};
	
	/**
	 * post 요청을 보낸다. 
	 * JSON형식의 Result 객체를 결과로 받을때만 사용한다.
	 */
	atto.post = function(url, data, onSuccess, onFailure) {
		var xhr = $.post(url, data, function(res){
			var result = $.parseJSON(res);
			if (result.success) {
				if ($.isFunction(onSuccess)) {
					onSuccess.call(result, result.body);	
				}
			} else {
				if ($.isFunction(onFailure)) {
					onFailure.call(result, result.errorCode, result.errorMessage);
				} else {
					atto.alert(result.errorMessage);
				}
			}
		});
		return xhr;
	};
	
	/**
	 * 데이터 맵
	 */
	var _dataMap = {};
	/**
	 * 데이터를 저장하거나, 반환한다.
	 */
	atto.data = function(key, data){
		if (data === undefined) {
			return _dataMap[key];
		} else {
			 _dataMap[key] = data;
		}
	};
	
	/**
	 * 주어진 문자열이 1000 단위를 넘어갈 경우 
	 * 1000 단위 마다 콤마 (,) 를 추가해서 반환한다.
	 * 소숫점은 2자리 까지 표시한다.
	 */
	atto.addCommas = function (nStr) {
		nStr += '';
		var x = nStr.split('.');
		var x1 = x[0], x2 = '';
		if (x.length > 1) {
			var tempNum = parseInt(x[1].substring(0, 3));
			x2 = '.' + Math.round(tempNum / 10);
		}
		var rgx = /(\d+)(\d{3})/;
		while (rgx.test(x1)) {
			x1 = x1.replace(rgx, '$1' + ',' + '$2');
		}
		return x1 + x2;
	},
	
	/**
	 * 목표Position(destTop) 까지 부드럽게 스크롤한다.
	 */
	atto.scrollTo = function(destTop, ignoreHeader){
		if (!ignoreHeader) {
			destTop -= 41;
		}
		var $window = $(window);
		var docHeight = $(document).height();
		var windowHeight = $window.height();
		if (windowHeight + destTop > docHeight) {
			destTop = docHeight - windowHeight;				
		}
		var currTop = $window.scrollTop();
		
		// 현재 위치와 많은 차이가 나지 않으면 한방에 끝
		if (Math.abs(currTop - destTop) < 10) {
			$window.scrollTop(destTop);
			return;
		}
		
		var count = 10;
		var sum = 0;
		for (var i=1; i<=count; i++) {
			sum += i;
		}
		var accel = (destTop - currTop) / sum; // 가속도
		var unit = accel * (count); // 한번에 움직일 거리
		var timer = window.setInterval(function(){
			currTop += unit;
			unit -= accel;
			$window.scrollTop(currTop);
			if (--count <= 0) {
				$window.scrollTop(destTop);
				window.clearInterval(timer);
			}
		}, 30);
	};
	
	/**
	 * Input, Textarea의 placeholder와 입력가능 남은수를 표시해준다. 
	 * @param $input 인풋 (Input, Textarea)
	 * @param param 
	 * 		$remain : 남은수를 표시할 곳
	 * 		onCheckLength : 길이 체크를 할때마다 호출될 함수
	 */
	atto.input = function($input, param){
		var placeholder = $input.attr('placeholder'); // 비었을때 표시해줄 메시지 (placeholder)
		var maxLength = parseInt($input.attr('maxLength') || '0');
		if (param) {
			var $remain = param.$remain;
			var onCheckLength = param.onCheckLength;
			placeholder = placeholder || param.placeholder || '';
			maxLength = maxLength || param.maxLength;
		}
		var timer;
		
		function checkRemainLen(){
			if ($remain && maxLength) {
				var length = $input.val().length;
				var remainLen = maxLength - length;
				$remain.text(remainLen + '자');
				if (remainLen < 0) {
					$input.addClass('error');
				} else {
					$input.removeClass('error');
				}
				if ($.isFunction(onCheckLength)) {
					onCheckLength(length);
				}
			}
		}
		$input.focus(function(){
			$input.addClass('focus');
			if (placeholder && $input.hasClass('empty')) {
				$input.removeClass('empty').val('');				
			}
			if ($remain && maxLength) {
				timer = window.setInterval(checkRemainLen, 300);
			}
		}).blur(function(){
			$input.removeClass('focus');
			if (!$.trim($input.val())) {
				$input.trigger('reset');
			} else {
				checkRemainLen();	
			}
			if (timer) {
				window.clearInterval(timer);
				timer = null;
			}
		});
		$input.bind('reset', function(){
			$input.addClass('empty').val('');
			checkRemainLen();
			$input.val(placeholder);
		});
		if (placeholder && !$input.val()) {
			$input.addClass('empty').val(placeholder);
		}
	};
	
	/**
	 * 로그인 레이어를 보여준다.
	 */
	atto.showLogin = function(){
		atto.require(['atto.common.login'], function(){
			atto.common.login.showLayer();			
		});
	};
	
	/**
	 * 로그인 여부를 반환하다. 로그인 상태가 아니면 로그인 레이어를 띄워준다.
	 */
	atto.checkLogin = function(){
		if (!atto.data('session')) {
			atto.showLogin();
			return false;
		} else {
			return true;
		}
	};
	
	/**
	 * busy indicator 를 보여준다.
	 */
	atto.wait = function($target, param){
		if (param===false) {
			var $layerDim = $target.data('layerDim');
			var $indicator = $target.data('indicator');
			if ($layerDim) {
				$layerDim.remove();
				$target.removeData('layerDim');
			}
			if ($indicator) {
				$indicator.remove();
				$target.removeData('indicator');
			}
			if ($target.data('opacity')) {
				$target.css('opacity', $target.data('opacity'));
				$target.removeData('opacity');
			}
		} else {
			param = $.extend({
				size : 's',
				bgTop : '50%',
				left : 'auto',
				top : 'auto',
				width : null,
				height : null,
				opacity : 0.5,
				overlay : true,
				zIndex : 300
			}, param);
			
			if ($target.data('indicator')) {
				return;
			}
			var objWidth = param.width || $target.width();
			var objHeight = param.height || $target.height();
			
			// dim 처리를 위한 layer
			if (param.overlay) {
				var $layerDim = $('<div />').css({
					backgroundColor : '#fff',
					width : objWidth,
					height : objHeight,
					position : 'absolute',
					left : param.left,
					top : param.top,
					zIndex : param.zIndex,
					opacity : param.opacity
				});
				$target.prepend($layerDim);
				$target.data('layerDim', $layerDim);
			}
			// indicator 표시를 위한 layer
			var $indicator = $('<div />').css({
				backgroundPosition : '50% ' + param.bgTop,
				backgroundImage : 'url(/image/common/ani_busy_indicator' + (param.size=='s' ? '_s' : '') + '.gif)',
				backgroundRepeat : 'no-repeat',
				width : objWidth,
				height : objHeight,
				position : 'absolute',
				left : param.left,
				top : param.top,
				zIndex : param.zIndex + 1,
				opacity : 1 // 가끔 IE 에서 안보이는 현상을 해결하기 위해 
			});
			// Dim 레이어가 없으면 target 을 Dim 시킨다.
			if (!param.overlay) {
				$target.data('opacity', $target.css('opacity'));
				$target.css('opacity', param.opacity);
			}
			$target.prepend($indicator);
			$target.data('indicator', $indicator);
		}
	};
	
	/**
	 * atto.ui.dialog.alert 호출
	 */
	atto.alert = function(message, onHide){
		atto.require(['atto.ui.dialog'], function(){
			atto.ui.dialog.alert(message, onHide);				
		});
	};
	
	$.ajaxSetup({cache:false});
	
	$(window.document).ready(function(){
		_callDeferedFunctions('domReady');
		$('a.require-login').click(function(ev){
			if (!atto.data('session')) {
				ev.preventDefault();
				atto.showLogin();
			}
		});
		$('a.service-ready').click(function(ev){
			ev.preventDefault();
			atto.alert('서비스 준비중입니다.');				
		});
		$('a.btn-back-page').click(function(ev){
			ev.preventDefault();
			history.back();
		});
		
		// top 버튼 관련
		var $btnTop = $('#footer a.btn-top');
		var $window = $(window);
		$btnTop.click(function(ev){
			ev.preventDefault();
			atto.scrollTo(0);
		});
		function _onScroll(){
			if ($window.scrollTop() > 100) {
				$btnTop.show();
			} else {
				$btnTop.hide();
			}	
		}
		$window.scroll(_onScroll);
		_onScroll();
	});
})(window);
